package com.safe.service.impl;

import com.safe.controller.CommonsController;
import com.safe.core.Constant;
import com.safe.dao.TrainDao;
import com.safe.dao.TrajectoryDao;
import com.safe.entity.Train;
import com.safe.entity.Trajectory;
import com.safe.entity.vo.TrajectoryVo;
import com.safe.exception.ParamErrorException;
import com.safe.service.TrajectoryService;
import com.safe.utils.BeanUtil;
import com.safe.utils.RedisCacheUtil;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.RoundingMode;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;

/**
 * TrajectoryServiceImpl
 * Created by 涂奕恒 on 2020/7/7 16:23.
 */
@Service
public class TrajectoryServiceImpl implements TrajectoryService {

    @Autowired
    private TrajectoryDao trajectoryDao;

    @Autowired
    private TrainDao trainDao;

    @Override
    public List<Trajectory> findAll() {
        return trajectoryDao.findAll();
    }

    @Override
    public Page<Trajectory> find(int pageNum, int pageSize) {
        return trajectoryDao.findAll(new PageRequest(pageNum - 1, pageSize, Sort.Direction.DESC, "id"));
    }

    @Override
    public Page<Trajectory> find(int pageNum) {
        return find(pageNum, Constant.PAGE_DEF_SIZE);
    }

    @Override
    public Trajectory getById(int id) {
        return trajectoryDao.findOne(id);
    }

    @Override
    public void deleteById(int id) {
        trajectoryDao.delete(id);
    }

    @Override
    public Trajectory create(Trajectory trajectory) {
        return trajectoryDao.save(trajectory);
    }

    @Override
    public Trajectory update(Trajectory trajectory) {
        return trajectoryDao.save(trajectory);
    }

    @Override
    @Transactional
    public void deleteAll(int[] ids) {
        for (int id : ids) {
            deleteById(id);
        }
    }

    @Override
    public double uploadPosition(Integer trainId,
                                 Double lon,
                                 Double lat,
                                 String direction,
                                 Long createTime) throws ParamErrorException {
        if (null == trainId || null == lon || null == lat) {
            throw new ParamErrorException();
        }

        NumberFormat nf = NumberFormat.getNumberInstance();
        //保留8位小数
        nf.setMaximumFractionDigits(8);
        //如果不需要四舍五入，可以使用RoundingMode.DOWN
        nf.setRoundingMode(RoundingMode.UP);
        lon = Double.parseDouble(nf.format(lon));
        lat = Double.parseDouble(nf.format(lat));

        Train train = trainDao.findOne(trainId);

        if (null == train) {
            throw new ParamErrorException();
        }

        try {
            // 将用户点位信息保存到redis
            RedisCacheUtil redisCacheUtil = getRedisUtil();
            List<TrajectoryVo> trajectoryVoList = findListFromRedis(train, redisCacheUtil);

            // 增加该点位之后不会达到预期值，此时将继续添加至redis
            TrajectoryVo trajectoryVo = new TrajectoryVo();
            trajectoryVo.setTrainId(trainId);

            trajectoryVo.setLon(lon + "");
            trajectoryVo.setLat(lat + "");

            trajectoryVo.setDirection(direction);

            trajectoryVo.setCreateTime(null == createTime ? System.currentTimeMillis() : createTime);

            trajectoryVoList.add(trajectoryVo);

            if (trajectoryVoList.size() >= Constant.POSITION_SIZE) {
                // 增加该点位之后将达到预期值，此时需要将所有的点位清空并保存到数据库轨迹表中
                trajectoryDao.save(changeVoList(trajectoryVoList, train));

                trajectoryVoList = new ArrayList<>();
            }

            String json = JSONArray.fromObject(trajectoryVoList).toString();
            redisCacheUtil.hSet("train_" + train.getId(), "train_" + train.getName(), json);

            trainDao.save(train);

            CommonsController.trajectoryMap.put(train.getId() + "", trajectoryVo);

            return 1;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return 0;
    }

    private static RedisCacheUtil getRedisUtil() {
        return (RedisCacheUtil) BeanUtil.getBean("redisCache");
    }

    // 根据用户信息在redis中查询指定的点位信息
    private static List<TrajectoryVo> findListFromRedis(Train train, RedisCacheUtil redisCacheUtil) {
        List<TrajectoryVo> list = new ArrayList<>();
        JSONObject jsonObject;

        String json = redisCacheUtil.hGet("train_" + train.getId(), "train_" + train.getName());
        if (StringUtils.isBlank(json)) {
            return list;
        }
        JSONArray jsonArray = JSONArray.fromObject(json);
        for (Object aJsonArray : jsonArray) {
            jsonObject = (JSONObject) aJsonArray;
            list.add(getByJsonObject(jsonObject));
        }

        return list;
    }

    // 将jsonObject格式的对象转换为轨迹Vo类
    private static TrajectoryVo getByJsonObject(JSONObject jsonObject) {
        TrajectoryVo trajectoryVo = new TrajectoryVo();
        trajectoryVo.setTrainId(jsonObject.containsKey("trainId") ? (Integer) jsonObject.get("trainId") : 0);
        trajectoryVo.setLon(jsonObject.containsKey("lon") ? jsonObject.get("lon").toString() : "");
        trajectoryVo.setLat(jsonObject.containsKey("lat") ? jsonObject.get("lat").toString() : "");
        trajectoryVo.setDirection(jsonObject.containsKey("direction") ? (String) jsonObject.get("direction") : "");
        trajectoryVo.setCreateTime(jsonObject.containsKey("createTime") ? Long.parseLong(jsonObject.get("createTime").toString()) : System.currentTimeMillis());

        return trajectoryVo;
    }

    // 将用户的信息和轨迹Vo类拼接为完整的轨迹信息
    private static Trajectory changeVoList(List<TrajectoryVo> voList, Train train) {
        Trajectory trajectory = new Trajectory();

        trajectory.setTrain(train);
        trajectory.setContent(JSONArray.fromObject(voList).toString());

        return trajectory;
    }
}